import 'package:badges/badges.dart' as badges;
import 'package:family_expense/features/account_user/services/account_group_service.dart';
import 'package:family_expense/features/account_user/services/join_account_group_request_service.dart';
import 'package:family_expense/features/account_user/widgets/account_group_user_home.dart';
import 'package:family_expense/features/account_user/widgets/account_group_user_home_app_bar.dart';
import 'package:family_expense/features/authentication/widgets/password_login.dart';
import 'package:family_expense/features/credit_card_bill/widgets/credit_card_bill_app_bar.dart';
import 'package:family_expense/features/credit_card_bill/widgets/credit_card_bill_home.dart';
import 'package:family_expense/features/home_view/widgets/home_view.dart';
import 'package:family_expense/features/home_view/widgets/home_view_app_bar.dart';
import 'package:family_expense/features/income_expense/widgets/edit_income_expense_detail.dart';
import 'package:family_expense/features/income_expense_analysis/widgets/income_expense_analysis_app_bar.dart';
import 'package:family_expense/features/income_expense_analysis/widgets/income_expense_analysis_home.dart';
import 'package:family_expense/features/income_expense_search/widgets/income_expense_search_home.dart';
import 'package:family_expense/features/system_setting/services/app_release_log_service.dart';
import 'package:family_expense/features/system_setting/widgets/app_permission_setting.dart';
import 'package:family_expense/features/system_setting/widgets/app_update_info.dart';
import 'package:family_expense/shared/constants/data_sharing_key.dart'
    as constants;
import 'package:family_expense/shared/services/quick_actions_service.dart';
import 'package:family_expense/shared/services/system_setting_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:my_flutter_core/my_flutter_core.dart';
import 'package:permission_handler/permission_handler.dart';

/// The main view contains the [BottomNavigationBar]. This is the default view after login.
///
/// Because the appBar, body and bottomNavigationBar are all based on [_selectedIndex].
/// Once the [_selectedIndex] changes they all need to rebuild.
/// That's why the [StatefulWidget] is used instead of the [Provider].
class MainView extends StatefulWidget {
  const MainView({super.key, this.redirectRoute});

  final Widget? redirectRoute;

  @override
  State<MainView> createState() => _MainViewState();
}

class _MainViewState extends State<MainView> {
  // The interval between double return action in seconds.
  final int _doublePressReturnInterval = 1;

  // The index is used to display the widget based on the selected menu.
  int _selectedIndex = 0;

  // Checks if join account group request exists.
  bool _hasReceivedJoinAccountGroupRequest = false;

  // Checks if app upate exists.
  bool _hasAppUpdate = false;

  bool _quickActionProceeded = false;

  // The last time user presses return button from this view.
  DateTime? _lastPressReturn;

  User? _currentUser;

  @override
  void initState() {
    super.initState();

    _loadCurrentUser();
    _initializeQuickActions();
    _checkOnRedirectRoute();
    _checkOnAppPermission();
    _queryReeceivedRequestCount();
    _setupReceivedRequestCountListener();
    _setupLogoutListener();

    // The build method triggered by navigation. If we put _checkUpdate() to FutureProvider directly
    // it would cause multiple check update requests during each build. That definitely is not what you want.
    // Put it here because the initState() is guaranteed to be called only once.
    //
    // Downloading apk file for upgrade is for Android ONLY.
    if (PlatformInfo.isAndroid) {
      // Setting the future in initState method triggers UI re-build.
      // It does not work when use setState inside _checkUpdate method.
      _checkAppUpdate();
    }
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvokedWithResult: _onPopInvokedWithResult,
      // The check update result retrieved from server for [bottomNavigationBar] and [AccountGroupUserHome] widget usage.
      child: Scaffold(
        appBar: _getAppBar(context, _selectedIndex),
        // The user info loaded from local for [AccountGroupUserHome] usage.
        body: _getBody(_selectedIndex),
        bottomNavigationBar: _buildButtomNavigationBar(),
        floatingActionButton: FloatingActionButton(
          onPressed: _currentUser?.isViewerOnly == true
              ? null
              : () => NavigatorHelper.push(
                  context,
                  const EditIncomeExpenseDetail(),
                ),
          child: _currentUser?.isViewerOnly == true
              ? Image.asset('assets/images/logo.png', width: 35.0)
              : const Icon(Icons.add),
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        extendBody: true, // CRITICAL for body flowing under FAB.
      ),
    );
  }

  /// Gets the [AppBar] for each displayed [Widget] based on [index].
  ///
  /// One [Widget] will be displayed at a time. The [index] will determine which one to display.
  AppBar? _getAppBar(BuildContext context, int index) {
    switch (index) {
      case 0:
        return HomeViewAppBar(
          actionCallback: _switchAccountGroup,
        ).getAppBar(context);
      case 1:
        return IncomeExpenseAnalysisAppBar().getAppBar(context);
      case 3:
        return CreditCardBillAppBar().getAppBar(context);
      case 4:
        return AccountGroupUserHomeAppBar().getAppBar(context);
      default:
        return null;
    }
  }

  /// Gets the content for each displayed [Widget] based on [index].
  ///
  /// One [Widget] will be displayed at a time. The [index] will determine which one to display.
  Widget? _getBody(int index) {
    switch (index) {
      case 0:
        // Setting a key to [HomeView] will force it to rebuild once current account group has changed.
        return HomeView(
          key: ValueKey(AccountGroupService().currentAccountGroup?.id),
        );
      case 1:
        return const IncomeExpenseAnalysisHome();
      case 3:
        return const CreditCardBillHome();
      case 4:
        return const AccountGroupUserHome();
      default:
        return null;
    }
  }

  /// Builds the [BottomNavigationBar] for each displayed [Widget] based on [index].
  ///
  /// Notch works with BottomAppBar + BottomNavigationBar:
  /// https://stackoverflow.com/questions/59455684/how-to-make-bottomnavigationbar-notch-transparent
  Widget _buildButtomNavigationBar() {
    const iconSize = 22.0;

    return BottomAppBar(
      shape: const CircularNotchedRectangle(),
      notchMargin: 5.0,
      elevation: 0.0,
      child: NavigationBar(
        backgroundColor: Colors.transparent,
        elevation: 0.0,
        destinations: [
          const NavigationDestination(
            icon: Icon(Icons.home, size: iconSize),
            label: '首页',
          ),
          NavigationDestination(
            icon: Icon(MdiIcons.chartAreaspline, size: iconSize),
            label: '分析',
          ),
          // The 3rd [NavigationDestination] is just a placeholder for center docked FAB.
          const NavigationDestination(
            icon: SizedBox(),
            label: '',
            enabled: false,
          ),
          const NavigationDestination(
            icon: Icon(Icons.credit_card, size: iconSize),
            label: '账单',
          ),
          NavigationDestination(
            icon: badges.Badge(
              showBadge: _hasAppUpdate || _hasReceivedJoinAccountGroupRequest,
              badgeAnimation: const badges.BadgeAnimation.scale(),
              badgeContent: const Text(''),
              child: Icon(MdiIcons.account, size: iconSize),
            ),
            label: '我的',
          ),
        ],
        selectedIndex: _selectedIndex,
        onDestinationSelected: (index) {
          if (mounted) {
            setState(() => _selectedIndex = index);
          }
        },
      ),
    );
  }

  Future<void> _loadCurrentUser() async {
    // Loads current user from local storage.
    final res = await AuthStateService.instance.currentUser;

    if (mounted) {
      setState(() => _currentUser = res);
    }
  }

  void _switchAccountGroup(dynamic data) {
    if (mounted) {
      setState(() => _currentUser = data);
    }
  }

  /// [quick_actions] Navigator opens the page twice through shortcuts if app is not running.
  /// https://github.com/flutter/flutter/issues/131121
  ///
  /// We use this workround to avoid it from happening.
  bool _canQuickActionProceed() {
    if (_quickActionProceeded) {
      return false;
    }

    _quickActionProceeded = true;

    // Just makes sure [_quickActionProceeded] value is restored.
    Future.delayed(
      const Duration(seconds: 1),
    ).then((_) => _quickActionProceeded = false);

    return true;
  }

  /// Creates eponymous concept on iOS and App Shortcuts on Android.
  void _initializeQuickActions() {
    QuickActionsService.initialize((shortcutType) {
      if (_canQuickActionProceed()) {
        if (shortcutType == QuickActionsService.editIncomeExpenseDetail &&
            mounted) {
          NavigatorHelper.push(context, const EditIncomeExpenseDetail());
        } else if (shortcutType ==
                QuickActionsService.incomeExpenseSearchHome &&
            mounted) {
          NavigatorHelper.push(context, const IncomeExpenseSearchHome());
        } else if (shortcutType ==
                QuickActionsService.incomeExpenseAnalysisHome &&
            mounted) {
          // Sets the current view to [IncomeExpenseAnalysisHome].
          setState(() => _selectedIndex = 1);
        } else if (shortcutType == QuickActionsService.creditCardBillView &&
            mounted) {
          // Sets the current view to [CreditCardBillView].
          setState(() => _selectedIndex = 3);
        }
      }
    });
  }

  /// Checks on redirect route and makes navigation if needed.
  void _checkOnRedirectRoute() {
    if (widget.redirectRoute != null) {
      NavigatorHelper.push(context, widget.redirectRoute!);
    }
  }

  Future<void> _checkOnAppPermission() async {
    if (await Permission.camera.status != PermissionStatus.granted ||
        await Permission.locationWhenInUse.status != PermissionStatus.granted) {
      if (mounted) {
        NavigatorHelper.push(context, const AppPermissionSetting());
      }
    }
  }

  /// Checks if need to exist current app when the main view is popped.
  void _onPopInvokedWithResult(bool didPop, dynamic result) {
    if (didPop) {
      return;
    }

    // Resets to first tab if needed.
    // if (_selectedIndex != 0 && mounted) {
    //   setState(() => _selectedIndex = 0);

    //   return false;
    // }

    // Gets the system setting.
    SystemSettingService.setting.then((systemSetting) {
      // [_lastReturned] is null means user did not try to return yet.
      // Check if user did not try to returned yet or the difference between current time and last returned time is more than [_doubleReturnInterval].
      if (systemSetting.generalSetting!.doublePressExit &&
          (_lastPressReturn == null ||
              getCurrentDateTime().difference(_lastPressReturn!).inSeconds >
                  _doublePressReturnInterval)) {
        // Saves current return time.
        _lastPressReturn = getCurrentDateTime();

        // Shows the message to user.
        showToast('再按一次退出程序');
      } else {
        // Exits current app.
        SystemNavigator.pop();
      }
    });
  }

  Future<void> _queryReeceivedRequestCount() async {
    final res = await JoinAccountGroupRequestService().countReceived();

    if (mounted) {
      setState(() {
        _hasReceivedJoinAccountGroupRequest = res > 0;
      });
    }
  }

  /// Checks the app update info.
  Future<void> _checkAppUpdate() async {
    // Gets app current version.
    final appVersion = await getAppLocalVersion();

    // Checks for update by current version.
    final response = AppReleaseLogService().checkUpdate(appVersion);

    // Queries update result from service.
    final updateResult = await response;

    // For badge in bottom bar item.
    if (mounted) {
      setState(() => _hasAppUpdate = updateResult.hasUpdate == true);
    }

    // Gets result and caches it for cross view usage.
    DataSharing.set<CheckAppUpdateResult>(
      DataSharingKey.appCheckUpdateResult,
      updateResult,
    );

    // If this is a mandatory update then will go to [AppUpdateInfo] and leaves user with no choice but update app.
    // Procceed with caution when introducing mandatory update. Because this is not user friendly.
    if (updateResult.appInfo?.isMandatory == true && mounted) {
      // DO NOT use BuildContext across asynchronous gaps.
      // https://dart-lang.github.io/linter/lints/use_build_context_synchronously.html
      NavigatorHelper.pushAndRemoveUntil(
        context,
        AppUpdateInfo(updateResult),
        (route) => false,
      );
    }
  }

  void _setupReceivedRequestCountListener() {
    // Sets received request count listener to handle count change from [AccountGroupUserHome].
    final receivedRequestCountChangeNotifier = SimpleChangeNotifier<int>();

    receivedRequestCountChangeNotifier.addListener(() {
      if (mounted) {
        setState(
          () => _hasReceivedJoinAccountGroupRequest =
              receivedRequestCountChangeNotifier.value! > 0,
        );
      }
    });

    DataSharing.set(
      constants.DataSharingKey.receivedRequestCountChangeNotifier,
      receivedRequestCountChangeNotifier,
    );
  }

  /// Sets logout listener do handle unauthorized error from http request service and other logout actions.
  /// We need to do logout for such error but there is not [context] in http request service.
  /// That's why we use change notifier.
  ///
  /// This notifier replaced the same one in main.dart because that notifier only works during app startup.
  void _setupLogoutListener() {
    final logoutChangeNotifier = SimpleChangeNotifier<LogoutNotifier>(
      initialValue: LogoutNotifier(_logoutListener),
    );

    logoutChangeNotifier.addListener(_logoutListener);
    DataSharing.set(LogoutNotifier.key, logoutChangeNotifier);
  }

  void _logoutListener() {
    NavigatorHelper.pushAndRemoveUntil(
      context,
      const PasswordLogin(),
      (route) => false,
    );
  }
}
